home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 …SCII & the Runetime Code / ADC Developer CD (1992-07) (''Butch ASCII And The Runtime Code'')_iso / Dev.CD 199207.iso / Tools & Apps / Devices & Hardware / A⁄ROSE / MessageDispatcher / MessageDispatcher.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-05-07  |  14.5 KB  |  535 lines  |  [TEXT/MPS ]

  1. /*
  2.     Routines to implement a message dispatcher
  3.     
  4.     Written by: Anumele D. Raja and Mary Chan
  5.     
  6.     Date:        April 18, 1991
  7.     
  8.     Copyright @ 1991 Apple Computer, Inc.
  9. */
  10.  
  11. #include <Events.h>
  12.  
  13. #include "MessageDispatcher.h"
  14.  
  15. #include "MultiThread.h"
  16.  
  17. enum { kFalse, kTrue };
  18.  
  19. #define kUsed    0
  20. #define kFree    -1
  21. #define kMaxNumber 2147483647            // 2^31-1
  22.  
  23. //  Function declarations
  24.  
  25. mMessage *MatchingMessage(unsigned long msgId,
  26.                 tid_type msgFrom, unsigned short msgCode);
  27. int *EnterInDispatchTable(unsigned long msgId,
  28.     tid_type msgFrom, unsigned short msgCode, long timeOut, void (*complRout)(mMessage *));
  29. void DispatchComplRout(mMessage *msgPtr);
  30. void FreeDispatchTable(DispatchTable **disTblHdl);
  31. void ReturnAllMessages(MessageQueue **msgQHdl);
  32. DispatchTable **AllocateDispatchBlock(void);
  33.  
  34. /*  Externals:
  35.  
  36.     These are locations with in the 'mlti' resource in memory as part of
  37.         the MultiThread.a file.
  38.         
  39.     Only way to access them using PC relative addressing is to declare them as function calls.
  40. */
  41.  
  42. extern void messageQueueSize();
  43. extern void dispatchTblHdl();
  44. extern void messageQueueHdl();
  45.  
  46. /*
  47.  
  48.     Logic:
  49.     
  50.     SplReceive:
  51.     
  52.         If there is no completion routine then
  53.             If timeout value < 0 then
  54.                 If there is matching message in our queue then
  55.                     return with message
  56.                 else
  57.                     return 0
  58.             else  //  timeout value >= 0 then
  59.                 If there is matching message in our queue then
  60.                     return with message
  61.                 else
  62.                     Loop till you get a message or time out expires using Ticks
  63.                 
  64.         else        // there is compl    
  65.             If timeout value < 0 then
  66.                 If there is matching message in our queue then
  67.                     Call the completion routine with message
  68.                     return 0
  69.                 else
  70.                     return 0
  71.             else if timeout value >= 0 then
  72.                 If there is matching message in our queue then
  73.                     Call the completion routine with message
  74.                     return 0
  75.                 else    (Described in more detail in the code)
  76.                     Enter into the dispatch table with time out value
  77.                     Activate the entry after checking the message queue
  78.                     return 0
  79.     
  80.     Setup table entry:
  81.     
  82.         If DispatchTblHdl = 0 Then
  83.             Allocate a handle N entries of Dispatch table.
  84.             Put mID, mCode, mFrom, and timeout in DispatchTblHdl
  85.             Issue a Receive(OS_MATCH_ALL, OS_MATCH_ALL, OS_MATCH_ALL, 1, ourComplRout)
  86.         else
  87.             if there is a free entry then
  88.                 Put mID, mCode, mFrom, and timeout in DispatchTblHdl
  89.             else
  90.                 Allocate another handle N entries of Dispatch table.
  91.                 Chain it to the first one
  92.                 Put mID, mCode, mFrom, and timeout in DispatchTblHdl
  93.             return a pointer to the flag location
  94.                     
  95.     ourComplRout:
  96.     
  97.         if there is no message then
  98.             Go through table and count down timeouts
  99.             if any count becomes zero then
  100.                 call completion routine with 0
  101.                 Flag the entry as free
  102.                 If there is a pending entry then
  103.                     Issue a Receive(OS_MATCH_ALL, OS_MATCH_ALL, OS_MATCH_ALL, 1, ourComplRout)
  104.         else
  105.             Search through all the entries to check if there is a matching entry
  106.             If found then
  107.                 Call the completion routine corresponding to the match
  108.                 Flag the entry as free
  109.                 If there is a pending entry then
  110.                     Issue a Receive(OS_MATCH_ALL, OS_MATCH_ALL, OS_MATCH_ALL, 1, ourComplRout)
  111.             else
  112.                 Queue up this message
  113.                 Issue a Receive(OS_MATCH_ALL, OS_MATCH_ALL, OS_MATCH_ALL, 1, ourComplRout)
  114.  
  115. */
  116.  
  117. mMessage *SpecialReceive(unsigned long msgId, tid_type msgFrom,
  118.         unsigned short msgCode, long timeOut, void (*complRout)(mMessage *))
  119. {
  120.     mMessage *msgPtr;
  121.     unsigned long targetTime;
  122.     MessageQueue *msgQueue;
  123.     
  124.     //  Get pointer to the message queue
  125.     
  126.     msgQueue = **(MessageQueue ***)messageQueueHdl;
  127.  
  128.     // First check if there is a dispatch table allocated.  If not allocate one
  129.     
  130.     if (!*(DispatchTable ***)dispatchTblHdl) {        // No despatch table allocated
  131.         if ((*(DispatchTable ***)dispatchTblHdl = AllocateDispatchBlock()) != 0) {
  132.         
  133.             // If successfully allocated start the Receives
  134.         
  135.             // There was no receive given so far. So issue a receive with a timout of 1 tick
  136.             
  137.             Receive(OS_MATCH_ALL, OS_MATCH_ALL, OS_MATCH_ALL, 1, DispatchComplRout);
  138.         }
  139.         
  140.     }
  141.     
  142.     //  If there is no completion routine specified in the receive call
  143.     //        it should work like a normal receive and check for a message
  144.  
  145.     if (complRout == 0) {
  146.         if (timeOut < 0) {
  147.             if ((msgPtr = MatchingMessage(msgId, msgFrom, msgCode)) != 0)
  148.                 return msgPtr;
  149.             else
  150.                 return 0;
  151.         } else {
  152.             if ((msgPtr = MatchingMessage(msgId, msgFrom, msgCode)) != 0)
  153.                 return msgPtr;
  154.             else {
  155.                 if (timeOut != 0)
  156.                     targetTime = TickCount() + timeOut;
  157.                 else
  158.                     targetTime = kMaxNumber;
  159.                 while (targetTime < TickCount()) {
  160.                     if ((msgPtr = MatchingMessage(msgId, msgFrom, msgCode)) != 0)
  161.                         return msgPtr;
  162.                 }
  163.                 return 0;
  164.             }
  165.         }
  166.     }
  167.     
  168.     // If There is a completion routine specified
  169.     
  170.     else {        // There is completion routine
  171.         if (timeOut < 0) {
  172.             if ((msgPtr = MatchingMessage(msgId, msgFrom, msgCode)) != 0) {
  173.                 complRout(msgPtr);
  174.                 return 0;
  175.             }
  176.             else {
  177.                 return 0;
  178.             }
  179.             
  180.         } else {
  181.         
  182.         /*
  183.             The following code might look strange but it is necessary because of the
  184.                 following condition.
  185.                 
  186.             Assume a receive has come in with some selection.
  187.             
  188.             First we check if it is the message queue and if it is not we enter into
  189.                 the dispatch table.
  190.                 
  191.             Just after we check the message queue and just before we activate the
  192.                 dispatch table entry the completion routine may get the message.
  193.                 It checks to see if there is an entry in the dispatch table.
  194.                 There won't be an entry because we are just about to enter it.
  195.                 So the completion routines puts the message into the message queue.
  196.                 We, in the main line, do not know that and return.
  197.                 
  198.             To avoid missing a mesage we should check the message queue again after
  199.                 making an entry.  This creates another problem.  If we check the
  200.                 message queue after making an entry another message might which
  201.                 will be active the entry.
  202.                 
  203.             To solve this problem we should lock out the dispatch table and the message
  204.                 queue from the time we check the message queue and to the time we
  205.                 activate an entry.
  206.                 
  207.             We can't do it because completion routine has to either put a message into
  208.                 the queue or activate an entry in the table.
  209.                 
  210.             The solution followed is simple:
  211.             
  212.                 Just before looking for matching message in the message queue look
  213.                 for a free entry in the dispatch table and copy the message selection
  214.                 into it.  Then get a pointer to the flag word.
  215.                 
  216.                 Copy the fMsgCount from the message queue into the flag word.
  217.                 Because an active entry in the dispatch table is assumed to be a flag
  218.                 of 0 this will leave it as an inactive entry.  (When the message
  219.                 queue is initializd the fMsgCount is started with 1 which is a non
  220.                 zero value).
  221.                 
  222.                 If a matching message is not found in the message queue subtract the
  223.                 fMsgCount into the flag word.  Because we are using register as the
  224.                 pointer to the flag word the compiler will generate an instruction
  225.                 
  226.                             SUB.L    D0, (Ax)
  227.                             
  228.                 If the message count has not been incremented in the meantime the flag
  229.                 word becomes zero thus activating the entry.  If a message did get into
  230.                 the queue after we had checked the message queue then the flag word
  231.                 will be non zero and still inactive.
  232.         */
  233.                 
  234.             register int *flagPtr;
  235.             
  236.             flagPtr = EnterInDispatchTable(msgId, msgFrom, msgCode, timeOut, complRout);
  237.             
  238.             if (flagPtr == 0)
  239.                 return (mMessage *)kFailedReceive;
  240.             
  241.             while (*flagPtr) {                // Do as long as there is a new message coming in
  242.                 *flagPtr = msgQueue->fMsgCount;
  243.                 if ((msgPtr = MatchingMessage(msgId, msgFrom, msgCode)) != 0) {
  244.                     complRout(msgPtr);
  245.                     return 0;
  246.                 }
  247.                 *flagPtr -= msgQueue->fMsgCount;    // subtract the message count
  248.             }
  249.         }
  250.     }
  251. }
  252.  
  253. /*
  254.     Routine to look for matching message if any
  255. */
  256.  
  257. mMessage *MatchingMessage(unsigned long msgId,
  258.                 tid_type msgFrom, unsigned short msgCode)
  259. {
  260.     MessageQueue    *msgQueue;
  261.     mMessage        *msgPtr;
  262.     int                i;
  263.  
  264.     msgQueue = **(MessageQueue ***)messageQueueHdl;
  265.     if (msgQueue) {
  266.         for (i = 0; i < *(int *)messageQueueSize; i++) {
  267.             if (msgPtr = msgQueue->fMsgPtr[i]) {
  268.                 if ((msgId == OS_MATCH_ALL || msgPtr->mId == msgId) &&
  269.                     (msgFrom == OS_MATCH_ALL || msgPtr->mFrom == msgFrom) &&
  270.                     (msgCode == OS_MATCH_ALL || msgPtr->mCode == msgCode)) {
  271.                     msgQueue->fMsgPtr[i] = 0;        // take the message out of the queue
  272.                     return msgPtr;
  273.                 }
  274.             }
  275.         }
  276.     }
  277.     return 0;
  278. }
  279.  
  280. /*
  281.     Routine to set up the dispatch table
  282. */
  283.  
  284. int *EnterInDispatchTable(unsigned long msgId,
  285.     tid_type msgFrom, unsigned short msgCode, long timeOut, void (*complRout)(mMessage *))
  286. {
  287.     DispatchTable *disPtr;
  288.     DispatchTable **disHdl;
  289.     int i;
  290.  
  291.     if (disHdl = *(DispatchTable ***)dispatchTblHdl) {        // If there is despatch table
  292.         while (disHdl) {
  293.  
  294.             // Get pointer from the handle
  295.  
  296.             disPtr = (DispatchTable *)*disHdl;
  297.  
  298.             // Search through the array to find a free entry
  299.  
  300.             for (i = 0; i < kNoOfDispatchEntriesInBlock; i++) {
  301.                 if (disPtr->disEntry[i].fFlag != kUsed) {        // is this a free entry
  302.                     disPtr->disEntry[i].fMsgId = msgId;
  303.                     disPtr->disEntry[i].fMsgFrom = msgFrom;
  304.                     disPtr->disEntry[i].fMsgCode = msgCode;
  305.                     disPtr->disEntry[i].fTimeOut = timeOut;
  306.                     disPtr->disEntry[i].fComplRout = complRout;
  307.                     return &disPtr->disEntry[i].fFlag;
  308.                 }
  309.             }
  310.             
  311.             // No free entry found try to check the next block
  312.             
  313.             if (!disPtr->fNext) {        // If the next block is not yet allocated
  314.                 disPtr->fNext = AllocateDispatchBlock();    // allocate it and put handle in fNext
  315.                 disHdl = disPtr->fNext;        // get the pointer (if allocation fails will quit)
  316.             }
  317.             else {
  318.                 disHdl = disPtr->fNext;
  319.             }
  320.         }
  321.     }
  322.     return 0;
  323. }
  324.  
  325. //  Completion routine for the dispatcher
  326.  
  327. void DispatchComplRout(mMessage *msgPtr)
  328. {
  329.     DispatchTable    *disPtr;
  330.     int        i;
  331.     MessageQueue    *msgQueue;
  332.     unsigned long    msgId;
  333.     tid_type        msgFrom;
  334.     unsigned short    msgCode;    
  335.     
  336.     disPtr = **(DispatchTable ***)dispatchTblHdl;
  337.  
  338.     //  If the message pointer is zero then time out messages
  339.     
  340.     if (!msgPtr) {
  341.         while (disPtr) {
  342.             for (i = 0; i < kNoOfDispatchEntriesInBlock; i++) {
  343.                 if (disPtr->disEntry[i].fFlag == kUsed) {        // is this an active entry
  344.                     if (disPtr->disEntry[i].fTimeOut != kMaxNumber) {  // there is a time out
  345.                         if (--disPtr->disEntry[i].fTimeOut <= 0) {        // decrement timeout and
  346.                             if (disPtr->disEntry[i].fComplRout == 0)    // verify completion routine is ther
  347.                                 Debugger();
  348.                             else
  349.                                 disPtr->disEntry[i].fComplRout(0);        // if zero call completion routine with 0
  350.                             disPtr->disEntry[i].fFlag = kFree;
  351.                         }
  352.                     }
  353.                 }
  354.             }
  355.             if (disPtr->fNext)
  356.                 disPtr = *disPtr->fNext;
  357.             else
  358.                 disPtr = 0;
  359.         }
  360.     }
  361.     
  362.     //  If the message pointer is not zero then check if there is someone waiting
  363.     //        in the Dispatch table
  364.     
  365.     else {
  366.         while (disPtr && msgPtr) {
  367.             for (i = 0; i < kNoOfDispatchEntriesInBlock && msgPtr; i++) {
  368.                 if (disPtr->disEntry[i].fFlag == kUsed) {        // is this an active entry
  369.                     msgId = disPtr->disEntry[i].fMsgId;
  370.                     msgFrom = disPtr->disEntry[i].fMsgFrom;
  371.                     msgCode = disPtr->disEntry[i].fMsgCode;
  372.                     if ((msgId == OS_MATCH_ALL || msgPtr->mId == msgId) &&
  373.                         (msgFrom == OS_MATCH_ALL || msgPtr->mFrom == msgFrom) &&
  374.                         (msgCode == OS_MATCH_ALL || msgPtr->mCode == msgCode)) {
  375.                         if (disPtr->disEntry[i].fComplRout == 0)
  376.                             Debugger();
  377.                         else
  378.                             disPtr->disEntry[i].fComplRout(msgPtr);        // if zero call completion routine with 0
  379.                         msgPtr = 0;                                    // message passed on
  380.                         disPtr->disEntry[i].fFlag = kFree;
  381.                     }
  382.                 }
  383.             }
  384.             
  385.             //  Try the next Dispatch Table block
  386.             
  387.             if (disPtr->fNext)
  388.                 disPtr = *disPtr->fNext;
  389.             else
  390.                 disPtr = 0;
  391.         }
  392.         
  393.         //  There was non one waiting for this message so put it in the message queue
  394.         
  395.         if (msgPtr)    {                                // message not passed on
  396.             msgQueue = **(MessageQueue ***)messageQueueHdl;
  397.             if (msgQueue) {
  398.                 for (i = 0; i < *(int *)messageQueueSize && msgPtr; i++) {
  399.                     if (!msgQueue->fMsgPtr[i]) {
  400.                         msgQueue->fMsgCount++;            // Increment message count
  401.                         msgQueue->fMsgPtr[i] = msgPtr;
  402.                         msgPtr = 0;
  403.                     }
  404.                 }
  405.             }
  406.             
  407.             if (msgPtr) {            // there is no space to put the message in queue
  408.                 tid_type temp;
  409.                 temp = msgPtr->mFrom;
  410.                 msgPtr->mFrom = msgPtr->mTo;
  411.                 msgPtr->mTo = temp;
  412.                 msgPtr->mCode |= 0x8000;
  413.                 msgPtr->mStatus = 0x8000;    // return message as undeliverable
  414.                 Send(msgPtr);                // Which send does it use (the current one)
  415.             }
  416.         }
  417.     }
  418.  
  419.     //  Issue another receive with completion
  420.  
  421.     Receive(OS_MATCH_ALL, OS_MATCH_ALL, OS_MATCH_ALL, 1, DispatchComplRout);
  422. }
  423.  
  424. //  Free dispatch table in resonse to CloseDispatch
  425.  
  426. void FreeDispatchTable(DispatchTable **disTblHdl)
  427. {
  428.     DispatchTable    *disPtr;
  429.     int        i;
  430.  
  431.     if ((*disTblHdl)->fNext) {
  432.         FreeDispatchTable((*disTblHdl)->fNext);        // Free Dispatch table recursively
  433.     }
  434.     
  435.     //  First set all entries to be free so that completion routine does not use them
  436.     
  437.     disPtr = *disTblHdl;
  438.     for (i = 0; i < kNoOfDispatchEntriesInBlock; i++) {
  439.         disPtr->disEntry[i].fFlag = kFree;
  440.     }
  441.     HUnlock((Handle)disTblHdl);
  442.     DisposHandle((Handle)disTblHdl);
  443. }
  444.  
  445. //  Free all messages in the message queue
  446.  
  447. void ReturnAllMessages(MessageQueue **msgQHdl)
  448. {
  449.     MessageQueue    *msgQueue;
  450.     int     i;
  451.     
  452.     msgQueue = *msgQHdl;
  453.     if (msgQueue) {
  454.         for (i = 0; i < *(int *)messageQueueSize; i++) {
  455.             if (msgQueue->fMsgPtr[i]) {
  456.                 FreeMsg(msgQueue->fMsgPtr[i]);
  457.             }
  458.         }
  459.     }
  460.     HUnlock((Handle)msgQHdl);
  461.     DisposHandle((Handle)msgQHdl);
  462. }
  463.  
  464. // Allocate a handle for dispatch table
  465.  
  466. DispatchTable **AllocateDispatchBlock(void)
  467. {
  468.     Handle newHdl;
  469.     DispatchTable    *disPtr;
  470.     int i;
  471.     
  472.     newHdl = NewHandle(sizeof(DispatchTable));
  473.     
  474.     if (newHdl) {
  475.         MoveHHi(newHdl);
  476.         HLock(newHdl);
  477.         
  478.         // Set all entries to Free
  479.         
  480.         disPtr = *(DispatchTable **)newHdl;
  481.         
  482.         for (i = 0; i < kNoOfDispatchEntriesInBlock; i++) {
  483.             disPtr->disEntry[i].fFlag = kFree;
  484.         }
  485.         disPtr->fNext = 0;        // next block is not there
  486.     }
  487.         
  488.     return (DispatchTable **)newHdl;
  489. }
  490.  
  491. //  Open Dispatch (the size of the message queue is passed as the parameter)
  492.  
  493. void OpenDispatch(int queueSize)
  494. {
  495.     Handle    msgQHdl;
  496.     MessageQueue *msgQueue;
  497.     int    i;
  498.  
  499.     *(int *)messageQueueSize = queueSize;
  500.     *(Handle *)messageQueueHdl = msgQHdl = NewHandle(queueSize*sizeof(MessageQueue)+sizeof(int));
  501.     
  502.     if (msgQHdl) {
  503.         MoveHHi(msgQHdl);
  504.         
  505.         HLock(msgQHdl);
  506.         
  507.         msgQueue = *(MessageQueue **)msgQHdl;
  508.         for (i = 0; i < queueSize; i++)        // clear all the message pointers in queue
  509.             msgQueue->fMsgPtr[i] = 0;
  510.             
  511.         msgQueue->fMsgCount = 1;
  512.     }
  513.     
  514.     SetSpecialReceive();
  515. }
  516.  
  517. //  Close Dispatch
  518.  
  519. void CloseDispatch()
  520. {
  521.     DispatchTable **disHdl;
  522.     MessageQueue **msgQHdl;
  523.  
  524.     KillReceive();                    // Kill Receive
  525.     
  526.     RestoreOriginalReceive();
  527.     
  528.     if (disHdl = *(DispatchTable ***)dispatchTblHdl) {        // If there is despatch table
  529.         FreeDispatchTable(disHdl);
  530.         if (msgQHdl = *(MessageQueue ***)messageQueueHdl) {
  531.             ReturnAllMessages(msgQHdl);
  532.         }
  533.     }
  534. }
  535.